/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.keyguard;

import static com.android.systemui.DejankUtils.whitelistIpcs;

import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Button;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.R;
import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.util.EmergencyDialerConstants;
import com.unisoc.keyguard.KeyguardSimLockMonitor;

import java.util.List;

/**
 * This class implements a smart emergency button that updates itself based
 * on telephony state.  When the phone is idle, it is an emergency call button.
 * When there's a call in progress, it presents an appropriate message and
 * allows the user to return to the call.
 */
public class EmergencyButton extends Button {

    private static final String LOG_TAG = "EmergencyButton";
    private final EmergencyAffordanceManager mEmergencyAffordanceManager;

    private int mDownX;
    private int mDownY;
    KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {

        @Override
        public void onSimStateChanged(int subId, int slotId, int simState) {
            updateEmergencyCallButton();
        }

        @Override
        public void onPhoneStateChanged(int phoneState) {
            updateEmergencyCallButton();
        }

        /* For SubsidyLock feature @{ */
        @Override
        public void onSubsidyDeviceLock(int mode) {
            updateEmergencyCallButton();
        }

        @Override
        public void onSubsidyEnterCode() {
            updateEmergencyCallButton();
        }
        /* @} */
    };

    private final StatusBarStateController.StateListener mStatusBarStateListener =
            new StatusBarStateController.StateListener() {
                @Override
                public void onDozingChanged(boolean isDozing) {
                    Log.d(LOG_TAG, "onDozingChanged isDozing " + isDozing);
                    mIsDozing = isDozing;
                    updateEmergencyCallButton();
                }
            };

    private boolean mLongPressWasDragged;

    public interface EmergencyButtonCallback {
        public void onEmergencyButtonClickedWhenInCall();
    }

    private LockPatternUtils mLockPatternUtils;
    private PowerManager mPowerManager;
    private EmergencyButtonCallback mEmergencyButtonCallback;

    private final boolean mIsVoiceCapable;
    private final boolean mEnableEmergencyCallWhileSimLocked;

    private final int mShowBtnInLockScreen;
    private final StatusBarStateController mStatusBarStateController;
    private boolean mIsDozing;
    // UNISOC: during power on the phone, we cannot read ss from KeyguardUpdateMonitor,
    // we can use mStickySS to save servicestate broadcast the last time.
    private ServiceState mStickySS = null;
    private boolean mShowNoServiceInEmergency;

    public EmergencyButton(Context context) {
        this(context, null);
    }

    public EmergencyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        mIsVoiceCapable = getTelephonyManager().isVoiceCapable();
        mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked);
        mShowBtnInLockScreen = mContext.getResources().getInteger(
                R.integer.config_show_emergency_button_on_lockscreen);
        mShowNoServiceInEmergency = mContext.getResources().getBoolean(
                R.bool.config_show_no_service_in_emergency);
        mStatusBarStateController = Dependency.get(StatusBarStateController.class);
        mIsDozing = mStatusBarStateController.isDozing();
        mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
    }

    private TelephonyManager getTelephonyManager() {
        return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback);
        mStatusBarStateController.addCallback(mStatusBarStateListener);
        if (mShowNoServiceInEmergency) {
            final IntentFilter filter = new IntentFilter();
            filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
            mContext.registerReceiver(mReceiver, filter);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback);
        mStatusBarStateController.removeCallback(mStatusBarStateListener);
        if (mShowNoServiceInEmergency) {
            mContext.unregisterReceiver(mReceiver);
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mLockPatternUtils = new LockPatternUtils(mContext);
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        setOnClickListener(v -> {
            if (!EmergencyButton.this.isVisibleToUser()) {
                return;
            }
            takeEmergencyCallAction();
        });
        if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
            setOnLongClickListener(v -> {
                if (!EmergencyButton.this.isVisibleToUser()) {
                    return false;
                }
                if (!mLongPressWasDragged
                        && mEmergencyAffordanceManager.needsEmergencyAffordance()) {
                    mEmergencyAffordanceManager.performEmergencyCall();
                    return true;
                }
                return false;
            });
        }
        whitelistIpcs(this::updateEmergencyCallButton);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int x = (int) event.getX();
        final int y = (int) event.getY();
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
            mDownX = x;
            mDownY = y;
            mLongPressWasDragged = false;
        } else {
            final int xDiff = Math.abs(x - mDownX);
            final int yDiff = Math.abs(y - mDownY);
            int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
            if (Math.abs(yDiff) > touchSlop || Math.abs(xDiff) > touchSlop) {
                mLongPressWasDragged = true;
            }
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean performLongClick() {
        return super.performLongClick();
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (isInsideKeyguardBottomArea()) {
            Log.d(LOG_TAG, "onConfigurationChanged text_size = " + getResources().getDimensionPixelSize(
                    com.android.internal.R.dimen.text_size_small_material) );
            this.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(
                    com.android.internal.R.dimen.text_size_small_material));
        }
        updateEmergencyCallButton();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (!isVisibleToUser()) {
            return false;
        }
        return super.dispatchTouchEvent(event);
    }

    /**
     * Shows the emergency dialer or returns the user to the existing call.
     */
    public void takeEmergencyCallAction() {
        MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_CALL);
        if (mPowerManager != null) {
            mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
        }
        try {
            ActivityTaskManager.getService().stopSystemLockTaskMode();
        } catch (RemoteException e) {
            Slog.w(LOG_TAG, "Failed to stop app pinning");
        }
        if (isInCall()) {
            resumeCall();
            if (mEmergencyButtonCallback != null) {
                mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
            }
        } else {
            KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
            if (updateMonitor != null) {
                updateMonitor.reportEmergencyCallAction(true /* bypassHandler */);
            } else {
                Log.w(LOG_TAG, "KeyguardUpdateMonitor was null, launching intent anyway.");
            }
            TelecomManager telecomManager = getTelecommManager();
            if (telecomManager == null) {
                Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer");
                return;
            }
            Intent emergencyDialIntent =
                    telecomManager.createLaunchEmergencyDialerIntent(null /* number*/)
                            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                                    | Intent.FLAG_ACTIVITY_CLEAR_TOP)
                            .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
                                    EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
            /* UNISOC: Modify for Bug1339523 @{ */
            final ActivityOptions opts = ActivityOptions.makeCustomAnimation(getContext(),
                    R.anim.emergency_dail_enter, R.anim.emergency_dail_exit);
            getContext().startActivityAsUser(emergencyDialIntent, opts.toBundle(),
                    new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
            /* @} */
        }
    }

    private void updateEmergencyCallButton() {
        boolean visible = false;
        if (mIsVoiceCapable) {
            if (isInsideKeyguardBottomArea()) {
                Log.d(LOG_TAG, "updateEmergencyCallButton isInsideKeyguardBottomArea");
                visible = showEmergencyButton(mShowBtnInLockScreen) && (!mIsDozing);
            } else {
                // Emergency calling requires voice capability.
                if (isInCall()) {
                    visible = true; // always show "return to call" if phone is off-hook
                } else {
                    final boolean simLocked = Dependency.get(KeyguardUpdateMonitor.class)
                            .isSimPinVoiceSecure();
                    // Unisoc: Support SimLock
                    final boolean isSimLockStateByUser = KeyguardSimLockMonitor.isSimLockStateByUser();
                    // For SubsidyLock feature
                    final boolean subSidyLocked = KeyguardSubsidyLockController.getInstance(mContext).isSubsidyLock();
                    if (simLocked) {
                        // Some countries can't handle emergency calls while SIM is locked.
                        visible = mEnableEmergencyCallWhileSimLocked;
                    }
                    // Unisoc: Support SimLock: Need to handle emergency calls while state is SIM LOCK.
                    else if(isSimLockStateByUser) {
                        visible = true;
                    } else if (subSidyLocked) { // When in SubsidyLock state, need show emergency button
                        visible = true;
                    } else {
                        // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk);
                        visible = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser());
                    }
                }
            }
        }
        if (visible) {
            setVisibility(View.VISIBLE);

            int textId;
            if (isInCall()) {
                textId = com.android.internal.R.string.lockscreen_return_to_call;
            } else {
                if (mShowNoServiceInEmergency && !isEmergencyCallAllowed()) {
                    textId = com.android.internal.R.string.lockscreen_carrier_default;
                    setClickable(false);
                    setLongClickable(false);
                } else {
                    textId = com.android.internal.R.string.lockscreen_emergency_call;
                    setClickable(true);
                    setLongClickable(true);
                }
            }
            setText(textId);
        } else {
            setVisibility(View.GONE);
        }
    }

    public void setCallback(EmergencyButtonCallback callback) {
        mEmergencyButtonCallback = callback;
    }

    /**
     * Resumes a call in progress.
     */
    private void resumeCall() {
        getTelecommManager().showInCallScreen(false);
    }

    /**
     * @return {@code true} if there is a call currently in progress.
     */
    private boolean isInCall() {
        return getTelecommManager().isInCall();
    }

    private TelecomManager getTelecommManager() {
        return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
    }

    private boolean showEmergencyButton(int emergencyButtonValue) {
        switch (emergencyButtonValue) {
            case 0:
                return true;
            case 1:
                boolean hasIccCard = false;
                int numPhones = getTelephonyManager().getPhoneCount();
                for (int i = 0; i < numPhones; i++) {
                    hasIccCard |= getTelephonyManager().hasIccCard(i);
                }
                boolean isAirPlaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
                if (isAirPlaneMode || !hasIccCard) {
                    return false;
                } else {
                    return true;
                }
            default:
                return false;
        }
    }

    private boolean isInsideKeyguardBottomArea() {
        return "emergency_call_btn_in_keyguard".equals(getTag());
    }

    private boolean isEmergencyCallAllowed() {
        List<SubscriptionInfo> subs = Dependency.get(KeyguardUpdateMonitor.class)
                .getSubscriptionInfo(false);
        final int N = subs.size();
        ServiceState ss = null;

        for (int i = 0; i < N; i++) {
            int subId = subs.get(i).getSubscriptionId();
            ss = Dependency.get(KeyguardUpdateMonitor.class)
                    .mServiceStates.get(subId);
            if (ss != null) {
                if (ss.isEmergencyOnly() || hasService(ss)) {
                    return true;
                }
            }
        }
        if(mStickySS != null) {
            if (mStickySS.isEmergencyOnly() || hasService(mStickySS)) {
                return true;
            }
        }

        return false;
    }

    private boolean hasService(ServiceState ss) {
        if (ss != null) {
            switch (ss.getState()) {
                case ServiceState.STATE_OUT_OF_SERVICE:
                    return false;
                case ServiceState.STATE_POWER_OFF:
                    return WirelessUtils.isAirplaneModeOn(mContext);
                default:
                    return true;
            }
        }
        return false;
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (mShowNoServiceInEmergency && (intent.getExtras() != null) &&
                    TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(intent.getAction())) {
                mStickySS = ServiceState.newFromBundle(intent.getExtras());
                Log.d(LOG_TAG, "ServiceStateChangedIntent: " + intent.getAction() +
                        " mStickySS= " + mStickySS);
                updateEmergencyCallButton();
            }
        }
    };
}
